package com.xyzwps.harbor.handler;

import org.springframework.web.reactive.function.server.HandlerFilterFunction;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public final class FiltersHelper {

    /**
     * 在一个 handler 外面包一个 filter 形成一个新 handler
     */
    public static HandlerFunction<ServerResponse> withFilter(
            HandlerFilterFunction<ServerResponse, ServerResponse> filterFn,
            HandlerFunction<ServerResponse> handlerFn
    ) {
        return new FilterDecoratedHandler(filterFn, handlerFn)::decorate;
    }

    @SafeVarargs
    public static HandlerFunction<ServerResponse> compose(
            HandlerFunction<ServerResponse> handlerFn,
            HandlerFilterFunction<ServerResponse, ServerResponse>... filterFnArray
    ) {
        return new FilterComposer(handlerFn, Arrays.asList(filterFnArray))::compose;
    }


    private static class FilterDecoratedHandler {
        private final HandlerFilterFunction<ServerResponse, ServerResponse> filterFn;
        private final HandlerFunction<ServerResponse> handlerFn;

        FilterDecoratedHandler(
                HandlerFilterFunction<ServerResponse, ServerResponse> filterFn,
                HandlerFunction<ServerResponse> handlerFn
        ) {
            this.filterFn = Objects.requireNonNull(filterFn, "decorator need a filter" );
            this.handlerFn = Objects.requireNonNull(handlerFn, "decorator need a handler" );
        }

        Mono<ServerResponse> decorate(ServerRequest req) {
            return filterFn.filter(req, handlerFn);
        }
    }

    private static class FilterComposer {

        private final HandlerFunction<ServerResponse> handler;
        private final List<HandlerFilterFunction<ServerResponse, ServerResponse>> filters;

        FilterComposer(
                HandlerFunction<ServerResponse> handler,
                List<HandlerFilterFunction<ServerResponse, ServerResponse>> filters
        ) {
            this.handler = Objects.requireNonNull(handler, "composer need a handler" );
            this.filters = Objects.requireNonNull(filters);
            this.initCheck();
        }

        private void initCheck() {
            if (filters.size() < 1) {
                throw new IllegalArgumentException("Filter composer need AT LEAST ONE FilterFn. You may not need a composer." );
            }
            for (int i = 0; i < filters.size(); i++) {
                Objects.requireNonNull(filters.get(i), "filters[" + i + "] is null" );
            }
        }

        Mono<ServerResponse> compose(ServerRequest req) {
            HandlerFunction<ServerResponse> composedHandler = withFilter(filters.get(filters.size() - 1), handler);
            for (int i = filters.size() - 2; i >= 0; i--) {
                composedHandler = withFilter(filters.get(i), composedHandler);
            }
            return composedHandler.handle(req);
        }
    }
}
